// Created by plusminus on 17:58:57 - 25.09.2008

package com.forestar.mapControl.mappool;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Environment;
import android.util.Log;

import androidx.annotation.NonNull;

import com.forestar.mapControl.utils.BitmapUtils;
import com.forestar.mapControl.utils.bitmapcache.BitmapLruCache;
import com.forestar.mapControl.utils.bitmapcache.CacheableBitmapDrawable;

import java.io.File;
import java.io.InputStream;

/**
 * A wrapper around a BitmapLruCache that stores tiles on disk in order to
 * improve performance and provide offline content.
 */
public class MzTileCache implements MzTileConstants
  {
    protected BitmapLruCache bitmapCache = null;
    static final String TAG = "MzTileCache";
    public static final String DISK_CACHE_SUBDIR = "cache";
    private static MzTileCache mzTileCache;
    private final Context context;
    private int mMaximumCacheSize;

    private boolean mDiskCacheEnabled = false;

    private MzTileCache(Context context, boolean diskCacheEnabled)
      {
        this(context, MZTILE_CACHE_DISK_CACHE_SIZE_DEFAULT, diskCacheEnabled);
      }

    private MzTileCache(Context context, int aMaximumCacheSize, boolean diskCacheEnabled)
      {
        this.mDiskCacheEnabled = diskCacheEnabled;
        this.mMaximumCacheSize = aMaximumCacheSize;
        this.context = context;
      }

    public static void init(Context context, int aMaximumCacheSize, boolean diskCacheEnabled)
      {
        mzTileCache = new MzTileCache(context, aMaximumCacheSize, diskCacheEnabled);
      }

    public static MzTileCache getInstance()
      {
        return mzTileCache;
      }

    protected BitmapLruCache getCache()
      {
        if (null == bitmapCache)
          {
            File cacheDir = getDiskCachePath(context);
            bitmapCache = (new BitmapLruCache.Builder(context)).setMemoryCacheEnabled(true)
                    .setMemoryCacheMaxSize(BitmapUtils.calculateMemoryCacheSize(context)).setDiskCacheEnabled(mDiskCacheEnabled)
                    .setDiskCacheMaxSize(mMaximumCacheSize).setDiskCacheLocation(cacheDir).build();
            Log.i(TAG, "Disk Cache Enabled: '" + bitmapCache.isDiskCacheEnabled() + "'; Memory Cache Enabled: '" + bitmapCache.isMemoryCacheEnabled() + "'");
          }
        return bitmapCache;
      }

    /**
     * Computes a prefixed key for a tile.
     *
     * @param tile the tile
     * @return the key
     */
    public String getCacheKey(MzTileBean tile)
      {
        return tile.getCacheKey();
      }

    public CacheableBitmapDrawable getMzTile(final MzTileBean tile)
      {
        String key = getCacheKey(tile);
        CacheableBitmapDrawable result = getCache().getFromMemoryCache(key);
        if (result == null)
          {
            result = getCache().getFromDiskCache(key, null);
          }
        return result;
      }

    public CacheableBitmapDrawable getMzTileFromMemory(final MzTileBean tile)
      {
        return getCache().getFromMemoryCache(getCacheKey(tile));
      }

    public CacheableBitmapDrawable getMzTileFromDisk(final MzTileBean tile)
      {
        return getCache().getFromDiskCache(getCacheKey(tile), null);
      }

    public CacheableBitmapDrawable putTileStream(final MzTileBean tile, final InputStream inputStream, final BitmapFactory.Options decodeOpts)
      {
        return getCache().put(getCacheKey(tile), inputStream, decodeOpts);
      }

    public CacheableBitmapDrawable putTileBitmap(final MzTileBean tile, final Bitmap bitmap)
      {
        return getCache().put(getCacheKey(tile), bitmap);
      }

    public CacheableBitmapDrawable putTile(final MzTileBean tile, final Drawable aDrawable)
      {
        if (aDrawable != null && aDrawable instanceof BitmapDrawable)
          {
            String key = getCacheKey(tile);
            CacheableBitmapDrawable drawable = null;
            if (!getCache().containsInMemoryCache(key))
              {
                drawable = getCache().putInMemoryCache(getCacheKey(tile), ((BitmapDrawable) aDrawable).getBitmap());
              }
            if (getCache().isDiskCacheEnabled() && !getCache().containsInDiskCache(key))
              {
                if (drawable != null)
                  {
                    getCache().putInDiskCache(getCacheKey(tile), drawable);
                  } else
                  {
                    getCache().putInDiskCache(getCacheKey(tile), ((BitmapDrawable) aDrawable).getBitmap());
                  }
              }
            return drawable;
          }
        return null;
      }

    public CacheableBitmapDrawable putTileInMemoryCache(final MzTileBean tile, final Bitmap aBitmap)
      {
        if (aBitmap != null)
          {
            return getCache().putInMemoryCache(getCacheKey(tile), aBitmap);
          }
        return null;
      }

    public CacheableBitmapDrawable putTileInMemoryCache(final MzTileBean tile, final Drawable aDrawable)
      {
        if (aDrawable != null && aDrawable instanceof BitmapDrawable)
          {
            String key = getCacheKey(tile);
            if (aDrawable instanceof CacheableBitmapDrawable)
              {
                return getCache().putInMemoryCache(key, ((CacheableBitmapDrawable) aDrawable));
              } else
              {
                return getCache().putInMemoryCache(key, ((BitmapDrawable) aDrawable).getBitmap());
              }
          }
        return null;
      }

    public CacheableBitmapDrawable putTileInDiskCache(final MzTileBean tile, final Drawable aDrawable)
      {
        if (aDrawable != null && aDrawable instanceof BitmapDrawable)
          {
            String key = getCacheKey(tile);
            if (getCache().isDiskCacheEnabled() && !getCache().containsInDiskCache(key))
              {
                return getCache().putInDiskCache(getCacheKey(tile), ((BitmapDrawable) aDrawable).getBitmap());
              }
          }
        return null;
      }

    public boolean containsTile(final MzTileBean tile)
      {
        return getCache().contains(getCacheKey(tile));
      }

    public boolean containsTileInDiskCache(final MzTileBean tile)
      {
        return getCache().isDiskCacheEnabled() && getCache().containsInDiskCache(getCacheKey(tile));
      }

    public void removeTile(final MzTileBean tile)
      {
        getCache().remove(getCacheKey(tile));
      }

    public void removeTileFromMemory(final MzTileBean tile)
      {
        String key = getCacheKey(tile);
        getCache().removeFromMemoryCache(key);
      }

    public void purgeMemoryCache()
      {
        getCache().purgeMemoryCache();
      }

    public void purgeDiskCache()
      {
        getCache().purgeDiskCache();
      }

    public CacheableBitmapDrawable createCacheableBitmapDrawable(Bitmap bitmap, MzTileBean tile)
      {
        return getCache().createCacheableBitmapDrawable(bitmap, getCacheKey(tile), CacheableBitmapDrawable.SOURCE_UNKNOWN);
      }

    public Bitmap getBitmapFromRemoved(final int width, final int height)
      {
        return getCache().getBitmapFromRemoved(width, height);
      }

    public Bitmap decodeBitmap(final byte[] data, final BitmapFactory.Options opts)
      {
        return getCache().decodeBitmap(new BitmapLruCache.ByteArrayInputStreamProvider(data), opts);
      }

    public Bitmap decodeBitmap(final BitmapLruCache.InputStreamProvider ip, final BitmapFactory.Options opts)
      {
        return getCache().decodeBitmap(ip, opts);
      }

    @NonNull
    public static File getDiskCachePath(Context context)
      {
        String cachePath = getCachePath(context);
        File cacheDir = new File(cachePath);
        if (!cacheDir.exists())
          {
            if (cacheDir.mkdirs())
              {
                Log.e(TAG, "can't create cacheDir " + cacheDir);
              }
          }
        return cacheDir;
      }

    @NonNull
    public static String getCachePath(Context context)
      {
         String cachePath = context.getExternalCacheDir() != null && (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || (!Environment.isExternalStorageRemovable())) ? context.getExternalCacheDir().getPath() : context.getCacheDir().getPath();
//        String cachePath = MapzoneConfig.getInstance(context).getMapCachePath();
        return cachePath;
      }

    public void setDiskCacheEnabled(final boolean enabled)
      {
        if (mDiskCacheEnabled != enabled)
          {
            mDiskCacheEnabled = enabled;
            bitmapCache = null;
          }
      }

    public boolean isDiskCacheEnabled()
      {
        return mDiskCacheEnabled;
      }

    public void resizeCacheMemory(int tileCount)
      {
        bitmapCache.resizeMemoryForTiles(tileCount);
      }

    public void close()
      {
        if(bitmapCache != null)
          {
            bitmapCache.close();
          }
      }
  }
